import express, { Request, Response, NextFunction, Application } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import morgan from 'morgan';
import path from 'path';
import fs from 'fs';
import { getLogger } from '@/utils/logger';
import ApiResponse from '@/utils/api/api-response.util';
import { operLogBefore, operLogAfter } from '@/middlewares/operLog.middleware';
import cacheUtils from '@/utils/cache/cache.util';
import compression from 'compression';
import  {DateUtil}  from '@/utils';

  const logger = getLogger("common-middleware", {
    console: false,
    errorFile:false,
  });
  const consoleLogger = getLogger("common-middleware", {
    console: true,
    errorFile:false,
  });

/**
 * 配置所有基础中间件
 * @param app Express应用实例
 */
export const setupMiddlewares = (app: Application): void => {
  // 1. 请求体解析中间件
  setupBodyParsers(app);
  
  // 2. CORS跨域支持
  setupCors(app);
  
  // 3. 静态资源服务
  setupStaticFiles(app);
  
  // 4. 安全头设置
  setupHelmet(app);
  
  // 5. 请求日志记录
  setupRequestLogging(app);
  
  // 6. 操作日志记录中间件
  setupOperLog(app);
  
  // 7. 响应压缩
  setupCompression(app);
  
  // 注意：错误处理中间件需要在所有路由之后配置
  // 将在应用主文件中配置
};

/**
 * 1. 设置请求体解析中间件
 */
const setupBodyParsers = (app: Application): void => {
  // JSON解析，限制请求体大小为10MB
  app.use(express.json({ limit: '10mb' }));
  
  // URL编码解析
  app.use(express.urlencoded({ 
    extended: true,  // 使用qs库解析复杂对象
    limit: '10mb'    // 限制大小
  }));
};

/**
 * 2. 设置CORS跨域支持
 */
const setupCors = (app: Application): void => {
  const corsOptions = {
    origin: (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => {
      // 获取白名单配置
      const whitelist = process.env.CORS_WHITELIST 
        ? process.env.CORS_WHITELIST.split(',') 
        : ['http://127.0.0.1:3000', 'http://localhost:3000', 'http://127.0.0.1:8620', 'http://localhost:8620'];
      // 如果白名单包含*，则允许所有来源
      if (whitelist.includes('*')) {
        callback(null, true);
        return;
      }
      
      // 允许没有origin的请求（如移动应用、Postman等）
      // 或者origin在白名单中的请求
      if (!origin || whitelist.indexOf(origin) !== -1) {
        callback(null, true);
      } else {
        // 记录被拒绝的请求来源，便于调试
        consoleLogger.warn(`CORS请求被拒绝: ${origin}`);
        callback(new Error('不允许的CORS请求'));
      }
    },
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
    credentials: true,               // 允许携带凭证
    optionsSuccessStatus: 200,       // 预检请求成功状态码
    allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
  };
  
  app.use(cors(corsOptions));
  
  // 处理预检请求的缓存
  app.options('*', cors(corsOptions));
};

/**
 * 3. 设置静态资源服务
 */
const setupStaticFiles = (app: Application): void => {
  // 配置上传文件目录为静态资源
  const uploadsDir = path.join(process.cwd(), process.env.UPLOAD_DIR || 'uploads');
  
  // 确保上传目录存在
  if (!fs.existsSync(uploadsDir)) {
    fs.mkdirSync(uploadsDir, { recursive: true });
  }
  
  // 配置静态资源服务
  app.use('/uploads', express.static(uploadsDir, {
    maxAge: 24 * 60 * 60 * 1000  // 缓存24小时
  }));
  
  // 配置静态资源目录
  const publicDir = path.join(process.cwd(), 'public');
  if (fs.existsSync(publicDir)) {
    app.use(express.static(publicDir, {
      maxAge: 24 * 60 * 60 * 1000  // 缓存24小时
    }));
  }
};

/**
 * 4. 设置安全头
 */
const setupHelmet = (app: Application): void => {
  // 基本安全头设置
  app.use(helmet());
  
  // 内容安全策略(CSP)
  app.use(
    helmet.contentSecurityPolicy({
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", "'unsafe-inline'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        imgSrc: ["'self'", "data:", "blob:"],
        fontSrc: ["'self'", "data:"],
      },
    })
  );
};

/**
 * 5. 设置请求日志记录
 */
const setupRequestLogging = (app: Application): void => {
  if (process.env.NODE_ENV !== 'development') {
		// 生产环境：将日志写入文件
		const logDir = path.join(process.cwd(), "logs");

		// 确保日志目录存在
		if (!fs.existsSync(logDir)) {
			fs.mkdirSync(logDir, { recursive: true });
		}

		// 创建日志写入流
		const accessLogStream = fs.createWriteStream(
			path.join(logDir, "access.log"),
			{ flags: "a" }
		);

		// 使用combined格式（包含更多信息）
		app.use(morgan("combined", { stream: accessLogStream }));
	}
  
  /**
   * 自定义morgan日志格式并集成到winston日志系统
   * 
   * 设计理念：
   * 1. 将HTTP请求日志与应用其他日志统一管理，便于关联分析和故障排查
   * 2. 利用winston的分类、级别和文件滚动等高级特性增强HTTP日志记录能力
   * 3. 自定义日志格式，提取最有价值的请求信息，便于监控和分析
   * 4. 过滤掉不必要的静态资源请求日志，减少日志噪音，聚焦于API请求
   */
  app.use(morgan((tokens, req, res) => {
    // 构建自定义日志消息，包含HTTP方法、URL、状态码、响应大小和处理时间
    // 格式：2025-06-23 23:51:26 info common-middleware GET /api/system/user/list?pageNum=1&pageSize=10 200  - 15.806 ms
    // 自定义 date 格式
    const date = DateUtil.getTime();
    const level = 'info';
    const category = '[common-middleware]';

    const logMessage = [
      date,                                   // 请求时间（2025-06-23 10:00:00）
      level,                                  // 日志级别（info、error、warn、debug）
      category,                               // 日志分类（common-middleware）
      tokens.method(req, res),                // HTTP方法（GET、POST等）
      tokens.url(req, res),                   // 请求URL
      tokens.status(req, res),                // HTTP状态码
      tokens.res(req, res, 'content-length'), // 响应内容大小（字节）
      '-',                                    // 分隔符
      tokens['response-time'](req, res), 'ms' // 请求处理时间（毫秒）
    ].join(' ');
    
    // 过滤掉静态资源请求的日志记录，减少日志量并聚焦于重要的API请求
    // 在大型应用中，静态资源请求量大，记录这些日志会淹没有价值的信息
    if (!req.url.startsWith('/uploads') && !req.url.startsWith('/public')) {
      // 使用winston记录日志，将其集成到应用的统一日志系统
      // 这样HTTP日志会与应用其他日志一起，遵循相同的日志级别、格式和存储策略
      logger.info(logMessage);
    }
    
    return logMessage;
  }));
};

/**
 * 6. 设置操作日志记录中间件
 */
const setupOperLog = (app: Application): void => {
  // 请求前处理中间件
  app.use(operLogBefore);
  
  // 请求后处理中间件 - 必须在路由之前配置
  app.use(operLogAfter);
};

/**
 * 7. 设置响应压缩
 */
const setupCompression = (app: Application): void => {
  // 配置响应压缩中间件
  app.use(compression());
};

/**
 * 全局错误处理中间件
 */
export const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction): Response | void => {
  // 记录错误
  consoleLogger.error(`错误: ${err.message}`);
  consoleLogger.error(`堆栈: ${err.stack}`);
  
  // 存储错误信息，以便操作日志中间件使用
  (res as any).locals = {
    errorMessage: err.message
  };
  
  // 检查响应头是否已发送，如果已发送则不再尝试发送响应
  if (res.headersSent) {
    return next(err);
  }
  
  // 根据环境返回不同级别的错误信息
  const isDev = process.env.NODE_ENV === 'development';
  
  // 使用API响应工具返回统一格式的错误信息
  return ApiResponse.error(
    res, 
    isDev ? err.message : '服务器内部错误', 
    500,
    isDev ? { stack: err.stack } : null
  );
};

/**
 * 404处理中间件
 */
export const notFoundHandler = (req: Request, res: Response): Response => {
  return ApiResponse.error(res, `找不到路径: ${req.originalUrl}`, 404);
};

/**
 * 防重提交中间件
 * 针对每个用户/请求生成 repeat_submit:{token}:{uri} key，短暂过期
 */
export function repeatSubmitMiddleware(expireSeconds = 5) {
  return async (req: Request, res: Response, next: NextFunction) => {
    const token = req.headers['authorization'] || req.ip;
    const uri = req.originalUrl;
    const key = `repeat_submit:${token}:${uri}`;
    const cacheResult = await cacheUtils.get(key);
    if (cacheResult.success && cacheResult.data) {
      return res.status(429).json({ code: 429, msg: '请勿重复提交', data: null });
    }
    await cacheUtils.set(key, "1", expireSeconds);
    next();
    return;
  };
}

/**
 * 限流中间件
 * 针对每个IP/接口生成 rate_limit:{ip}:{uri} key，统计单位时间内请求次数
 */
export function rateLimitMiddleware(limit = 10, windowSeconds = 30) {
  return async (req: Request, res: Response, next: NextFunction) => {
    const ip = req.ip;
    const uri = req.originalUrl;
    const key = `rate_limit:${ip}:${uri}`;
    const cacheResult = await cacheUtils.incr(key);
    if (cacheResult.success && typeof cacheResult.data === 'number' && cacheResult.data === 1) {
      await cacheUtils.expire(key, windowSeconds);
    }
    if (cacheResult.success && typeof cacheResult.data === 'number' && cacheResult.data > limit) {
      return res.status(429).json({ code: 429, msg: '请求过于频繁，请稍后再试', data: null });
    }
    next();
    return;
  };
} 